package com.xique.park.service.command.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xique.common.core.constant.CacheConstants;
import com.xique.common.core.constant.CommandResultCode;
import com.xique.common.core.constant.Constants;
import com.xique.common.core.constant.HttpStatus;
import com.xique.common.core.enums.CommandSyncCmd;
import com.xique.common.core.enums.SubDeviceModel;
import com.xique.common.core.utils.SecurityUtils;
import com.xique.common.core.utils.StringUtils;
import com.xique.common.core.utils.command.CommandResultUtil;
import com.xique.common.core.web.domain.AjaxResult;
import com.xique.common.core.web.domain.PageResult;
import com.xique.park.api.IParkCarMngTypeCommandApi;
import com.xique.park.api.IParkIpcCommandApi;
import com.xique.park.api.IParkLaneCommandApi;
import com.xique.park.bean.domain.*;
import com.xique.park.bean.request.web.garageLaneDev.board.VoiceSetRequest;
import com.xique.park.bean.request.web.garageLaneDev.board.VoiceTestRequest;
import com.xique.park.bean.request.web.garageLaneDev.subdev.DeleteSubDevRequest;
import com.xique.park.bean.request.web.ipc.CommandListRequest;
import com.xique.park.bean.request.web.ipc.DevDownAllRequest;
import com.xique.park.bean.vo.web.ipc.CommandInfoVo;
import com.xique.park.constant.CommandConstant;
import com.xique.park.constant.ParkApiName;
import com.xique.park.mapper.CommandInfoMapper;
import com.xique.park.service.biz.service.IParkGarageLaneDevBizService;
import com.xique.park.service.biz.service.IParkIpcBizService;
import com.xique.park.service.command.service.*;
import com.xique.park.service.service.*;
import com.xique.park.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 摄像机相关命令service
 *
 * @author xique
 */
@Slf4j
@Service
public class ParkIpcCommandServiceImpl extends CommandCommServiceImpl implements IParkIpcCommandService {

    @Autowired
    private IParkIpcCommandApi parkIpcCommandApi;

    @Autowired
    private IParkLaneService parkLaneService;

    @Autowired
    private IParkLaneCommandApi parkLaneCommandApi;

    @Autowired
    private ICommandInfoService commandInfoService;

    @Autowired
    private IParkConfigService parkConfigService;

    @Autowired
    private IParkConfigCommandService parkConfigCommandService;

    @Autowired
    private IParkGarageService parkGarageService;

    @Autowired
    private IParkGarageCommandService parkGarageCommandService;

    @Autowired
    private IParkIpcBizService parkIpcBizService;

    @Autowired
    private IParkLaneCommandService parkLaneCommandService;

    @Autowired
    private IParkDeviceNewService parkDeviceNewService;

    @Autowired
    private IParkDeviceSubService parkDeviceSubService;

    @Autowired
    private IParkCarMngtypeService parkCarMngtypeService;

    @Autowired
    private IParkCarMngTypeCommandService parkCarMngTypeCommandService;

    @Autowired
    private IParkCarMngtypeLaneService parkCarMngtypeLaneService;

    @Autowired
    private IParkChargeRuleCommService chargeRuleCommService;

    @Autowired
    private IParkChargeCarTempService chargeCarTempService;

    @Autowired
    private IParkChargeRuleCommandService chargeRuleCommandService;

    @Autowired
    private IParkCarCommandService parkCarCommandService;

    @Autowired
    private IParkCarService parkCarService;

    @Autowired
    private IParkGarageLaneDevBizService parkGarageLaneDevBizService;

    @Autowired
    private IParkCarMngTypeCommandApi parkCarMngTypeCommandApi;

    @Autowired
    private CommandInfoMapper commandInfoMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 下发失败的命令
     */
    private final Map<String, StringBuffer> commandFailMap = new ConcurrentHashMap<>();

    @Override
    public String getMainIpcStatus(String sn) {
        // TODO  下发命令之前需要判断设备是否在线
        return null;
    }

    @Override
    public AjaxResult devInit(String sn) {
        JSONObject obj = getBaseObj(SecurityUtils.getParkId(), sn, ParkApiName.COMMAND_DEV_INIT, ParkApiName.VER_1_0);
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        try {
            AjaxResult result = parkIpcCommandApi.devInit(params);
            return CommandResultUtil.handleCommandResult(result, "");
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("初始化失败", e.getMessage());
        }
    }

    /**
     * 副摄像机下发
     *
     * @param mainSn     主相机sn
     * @param subIpcList 副摄像机列表
     */
    @Override
    public AjaxResult downSubIpc(Long parkId, String mainSn, List<ParkDeviceNew> subIpcList, String reminderMsg) {
        JSONObject obj = getBaseObj(parkId, mainSn, ParkApiName.COMMAND_DOWN_SUB_IPC, ParkApiName.VER_1_0);
        obj.put(CommandConstant.DATA, JSON.toJSON(subIpcList));
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        AjaxResult result = parkIpcCommandApi.downSubIpc(params);
        return CommandResultUtil.handleCommandResult(result, reminderMsg);
    }

    @Override
    public AjaxResult downSubIpc(Long laneId, List<ParkDeviceNew> subIpcList, String reminderMsg) {
        String mainSn = getMainDeviceCodeByLaneId(SecurityUtils.getParkId(), laneId);
        return downSubIpc(SecurityUtils.getParkId(), mainSn, subIpcList, reminderMsg);
    }

    @Override
    public AjaxResult delSubIpc(String mainSn, DeleteSubDevRequest request, String reminderMsg) {
        JSONObject obj = getBaseObj(SecurityUtils.getParkId(), mainSn, ParkApiName.COMMAND_DEL_SUB_IPC, ParkApiName.VER_1_0);
        List<ParkDeviceNew> list = new ArrayList<>();
        ParkDeviceNew deviceNew = new ParkDeviceNew();
        deviceNew.setId(request.getId());
        list.add(deviceNew);
        obj.put(CommandConstant.DATA, JSON.toJSON(list));
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        AjaxResult result = parkIpcCommandApi.delSubIpc(params);
        return CommandResultUtil.handleCommandResult(result, reminderMsg);
    }

    @Override
    public AjaxResult delSubIpc(Long laneId, DeleteSubDevRequest request, String reminderMsg) {
        String mainSn = getMainDeviceCodeByLaneId(SecurityUtils.getParkId(), laneId);
        return delSubIpc(mainSn, request, reminderMsg);
    }

    /**
     * 下发附加设备
     */
    @Override
    public AjaxResult downSubDevs(Long parkId, String mainSn, List<ParkDeviceSub> subDevList, String reminderMsg) {
        JSONObject obj = getBaseObj(parkId, mainSn, ParkApiName.COMMAND_SET_DEV_PARAM, ParkApiName.VER_3_0);
        JSONObject dtObj = new JSONObject();
        // 控制板
        JSONObject boardObj = new JSONObject();
        // 剩余车位屏
        JSONObject remainDisplayObj = new JSONObject();
        // 扫描仪
        JSONObject scannerObj = new JSONObject();
        // ETC
        JSONObject etcObj = new JSONObject();
        JSONArray ids = new JSONArray(subDevList.size());
        for (ParkDeviceSub deviceSub : subDevList) {
            ids.add(deviceSub.getId());
            if ("scanner".equals(deviceSub.getType())) {
                // id不用下发给设备，只用于后端记录下发结果
                scannerObj.put("is_en", Constants.NORMAL.equals(deviceSub.getStatus()) ? 1 : 0);
                scannerObj.put("ip", deviceSub.getIp());
                scannerObj.put("type", deviceSub.getModel());
                scannerObj.put("port", StringUtils.isNotEmpty(deviceSub.getPort()) ? Integer.parseInt(deviceSub.getPort()) : null);
                dtObj.put("scanner", scannerObj);
            } else if ("remain_display".equals(deviceSub.getType())) {
                remainDisplayObj.put("is_en", Constants.NORMAL.equals(deviceSub.getStatus()) ? 1 : 0);
                remainDisplayObj.put("ip", deviceSub.getIp());
                remainDisplayObj.put("port", StringUtils.isNotEmpty(deviceSub.getPort()) ? Integer.parseInt(deviceSub.getPort()) : null);
                dtObj.put("remain_display", remainDisplayObj);
            } else if ("etc".equals(deviceSub.getType())) {
                etcObj.put("is_en", Constants.NORMAL.equals(deviceSub.getStatus()) ? 1 : 0);
                etcObj.put("type", deviceSub.getModel());
                etcObj.put("ip", deviceSub.getIp());
                etcObj.put("port", StringUtils.isNotEmpty(deviceSub.getPort()) ? Integer.parseInt(deviceSub.getPort()) : null);
                etcObj.put("is_test", deviceSub.getEtcTest());
                if (SubDeviceModel.TRS04.getCode().equals(deviceSub.getModel())) {
                    //浙江省ETC新增以下参数
                    etcObj.put("lane_no", deviceSub.getLaneNo());
                } else if (SubDeviceModel.HSWG.getCode().equals(deviceSub.getModel())) {
                    //浙江省ETC新增以下参数
                    etcObj.put("lane_no", deviceSub.getLaneNo());
                    etcObj.put("secret_key", deviceSub.getSecretKey());
                }
                dtObj.put("etc", etcObj);
            }
        }
        // 控制板处理
        ParkDeviceNew parkDeviceNew = parkDeviceNewService.selectParkDeviceNewByCode(mainSn);
        JSONArray boardArr = new JSONArray();
        if (StringUtils.isNotNull(parkDeviceNew)) {
            boardObj.put("port", 1);
            boardObj.put("type", StringUtils.isNotEmpty(parkDeviceNew.getControlType()) ? Integer.parseInt(parkDeviceNew.getControlType()) : null);
            boardObj.put("addr", 0);
            // 7:JZY-KF/TD(19200)
            if ("7".equals(parkDeviceNew.getControlType())) {
                boardObj.put("type", 2);
                boardObj.put("baud", 19200);
            } else if ("8".equals(parkDeviceNew.getControlType())) {
                boardObj.put("baud", 9600);
            } else if ("6".equals(parkDeviceNew.getControlType())) {
                boardObj.put("type", "3");
                boardObj.put("baud", 9600);
            } else {
                boardObj.put("baud", 115200);
            }
            boardArr.add(boardObj);
            dtObj.put("board", boardArr);
        }
        JSONObject dataObj = new JSONObject();
        dataObj.put("ids", ids);
        dataObj.put("dt", dtObj);
        obj.put(CommandConstant.DATA, dataObj);
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        AjaxResult result = parkIpcCommandApi.downSubDevs(params);
        return CommandResultUtil.handleCommandResult(result, reminderMsg);
    }

    @Override
    public AjaxResult downSubDevs(Long laneId, List<ParkDeviceSub> subDevList, String reminderMsg) {
        long parkId = SecurityUtils.getParkId();
        String mainSn = getMainDeviceCodeByLaneId(parkId, laneId);
        return downSubDevs(parkId, mainSn, subDevList, reminderMsg);
    }

    @Override
    public AjaxResult setVoice(Long laneId, VoiceSetRequest request, String reminderMsg) {
        JSONObject obj = getBaseObj(SecurityUtils.getParkId(), getMainDeviceCodeByLaneId(SecurityUtils.getParkId(), laneId), ParkApiName.COMMAND_SET_LANE_PARAM, ParkApiName.VER_2_0);
        ParkLane parkLane = parkLaneService.selectParkLaneById(request.getLaneId());
        parkLane.setParkId(SecurityUtils.getParkId());
        parkLane.setLowVoiceTime(request.getLowDuration());
        parkLane.setLowVoiceSound(request.getLowValue());
        parkLane.setHighVoiceTime(request.getHighDuration());
        parkLane.setHighVoiceSound(request.getHighValue());
        obj.put(CommandConstant.DATA, JSON.toJSON(parkLane));
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        AjaxResult result = parkLaneCommandApi.downParkLane(params);
        return CommandResultUtil.handleCommandResult(result, reminderMsg);
    }

    @Override
    public AjaxResult voiceTest(Long laneId, VoiceTestRequest request) {
        String mainSn = getMainDeviceCodeByLaneId(SecurityUtils.getParkId(), laneId);
        return voiceTest(mainSn, request);
    }

    @Override
    public AjaxResult voiceTest(String mainSn, VoiceTestRequest request) {
        JSONObject obj = getBaseObj(SecurityUtils.getParkId(), mainSn, ParkApiName.COMMAND_VOLUME_TEST, ParkApiName.VER_1_0);
        JSONObject dataObj = new JSONObject(1);
        dataObj.put("volume", request.getValue());
        obj.put(CommandConstant.DATA, dataObj);
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        AjaxResult result = parkIpcCommandApi.voiceTest(params);
        return CommandResultUtil.handleCommandResult(result, "");
    }

    @Override
    public AjaxResult setLed(Long parkId, ParkLane parkLane, String reminderMsg) {
        String mainSn = getMainDeviceCodeByLaneId(parkId, parkLane.getId());
        JSONObject obj = getBaseObj(parkId, mainSn, ParkApiName.COMMAND_SET_LED_PARAM, ParkApiName.VER_1_0);
        obj.put(CommandConstant.DATA, JSON.toJSON(parkLane));
        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);
        AjaxResult result = parkIpcCommandApi.setLed(params);
        return CommandResultUtil.handleCommandResult(result, reminderMsg);
    }

    /**
     * 一键同步
     */
    @Override
    public AjaxResult downAll(DevDownAllRequest request) {
        String sn = request.getDevCode();
        try {
            if (StrUtil.isNotBlank(commandFailMap.get(sn))) {
                return AjaxResult.success("命令正在下发，请稍等");
            }
            StringBuffer commandFailMsg = new StringBuffer();
            commandFailMap.put(sn, commandFailMsg);

            Long parkId = SecurityUtils.getParkId();
            List<Future<Boolean>> futureList = new ArrayList<>();

            // 待下发的命令
            Integer[] syncCmdS = request.getSyncCmd();
            CountDownLatch countDownLatch = new CountDownLatch(syncCmdS.length);
            Arrays.stream(syncCmdS).forEach(cmd -> {
                CommandSyncCmd commandSyncCmd = CommandSyncCmd.matchCmd(cmd);
                if (null != commandSyncCmd) {
                    log.info("[IParkIpcCommandService]一键同步数据，下发{}，车场ID：{}，设备编号：{}，命令：{}", CommandSyncCmd.matchName(cmd), parkId, sn, cmd);
                    switch (commandSyncCmd) {
                        case DOWN_SINGLE_DEV_PARK_CONFIG:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downSingleDevParkConfig(parkId, sn)));
                            break;
                        case DOWN_SINGLE_DEV_PARK_GARAGE_LIST:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downSingleDevParkGarageList(parkId, sn)));
                            break;
                        case DOWN_PARK_LANE:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downParkLane(parkId, sn)));
                            break;
                        case SET_LED:
                            futureList.add(threadPoolTaskExecutor.submit(() -> setLed(parkId, sn)));
                            break;
                        case DOWN_SUB_IPC:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downSubIpc(parkId, sn)));
                            break;
                        case ADDITIVE_EQUIPMENT:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downAdditiveEquipment(parkId, sn)));
                            break;
                        case DOWN_MNG_TYPE:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downMngType(parkId, sn)));
                            break;
                        case DOWN_MNG_TYPE_PASS_AUTH:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downMngTypePassAuth(parkId, sn)));
                            break;
                        case DOWN_CHARGE_RULE_COMM_LIST:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downChargeRuleAndTemp(parkId, sn)));
                            break;
                        case DOWN_CARS:
                            futureList.add(threadPoolTaskExecutor.submit(() -> downCars(parkId, sn)));
                            break;
                        default:
                            log.warn("[IParkIpcCommandService]一键同步数据，下发失败，命令错误：{}", cmd);
                            break;
                    }
                } else {
                    log.warn("[IParkIpcCommandService]一键同步数据，下发失败，命令错误：{}", cmd);
                }

                countDownLatch.countDown();
            });

            countDownLatch.await();

            boolean result = true;
            if (CollUtil.isNotEmpty(futureList)) {
                for (Future<Boolean> booleanFuture : futureList) {
                    if (Boolean.FALSE.equals(booleanFuture.get())) {
                        // 下发失败，有一个失败就返回失败
                        result = false;
                    }
                }
            }

            if (result) {
                return AjaxResult.success("下发成功");
            } else {
                StringBuffer errMsg = commandFailMap.get(sn);
                return AjaxResult.error("下发失败，" + Constants.BR + errMsg);
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            return AjaxResult.error("下发失败，接口异常");
        } finally {
            commandFailMap.remove(sn);
        }
    }

    /**
     * 下发车辆信息
     */
    private boolean downCars(Long parkId, String sn) {
        ParkCar parkCar = new ParkCar();
        parkCar.setParkId(parkId);
        List<ParkCar> carList = parkCarService.selectParkCarList(parkCar);
        if (CollectionUtils.isNotEmpty(carList)) {
            redisUtil.set(CacheConstants.UPGRADING + sn, Boolean.TRUE);

            AjaxResult carRes = parkCarCommandService.downCars(parkId, sn, carList);
            if (!carRes.get(Constants.RESULT_CODE).equals(CommandResultCode.OK)
                    && !carRes.get(Constants.RESULT_CODE).equals(CommandResultCode.REQ_RSP)
                    && !carRes.get(Constants.RESULT_CODE).equals(CommandResultCode.REQ_NORSP)) {
                String failMsg = (String) carRes.get(AjaxResult.MSG_TAG);
                log.warn("[IParkIpcCommandService#downCars]车辆信息下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
                String resultMsg = "车辆信息下发失败，" + failMsg + Constants.BR;
                StringBuffer commandFailMsg = commandFailMap.get(sn);
                commandFailMsg.append(resultMsg);
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }

    private boolean downChargeRuleAndTemp(Long parkId, String sn) {
        // 收费规则
        boolean downChargeRuleResult = downChargeRuleCommList(parkId, sn);
        // 车辆管理类型的收费规则
        boolean downChargeTempResult = downChargeTempList(parkId, sn);
        log.info("[IParkIpcCommandService#downChargeRuleAndTemp]一键同步数据，下发收费规则结果：{}，下发车辆管理类型的收费规则结果：{}", downChargeRuleResult, downChargeTempResult);
        boolean commandResult;
        if (downChargeRuleResult && downChargeTempResult) {
            commandResult = Boolean.TRUE;
        } else {
            commandResult = Boolean.FALSE;
        }
        return commandResult;
    }

    /**
     * 下发车辆管理类型的收费规则
     */
    private boolean downChargeTempList(Long parkId, String sn) {
        ParkChargeCarTemp temp = new ParkChargeCarTemp();
        temp.setParkId(parkId);
        List<ParkChargeCarTemp> parkChargeCarTempList = chargeCarTempService.selectParkChargeCarTempList(temp);
        AjaxResult chargeTempRes = chargeRuleCommandService.downChargeTempList(parkId, sn, parkChargeCarTempList);
        if (chargeTempRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) chargeTempRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downChargeTempList]收费规则适用的管理类型和车库下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "收费规则下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发收费规则
     */
    private boolean downChargeRuleCommList(Long parkId, String sn) {
        ParkChargeRuleComm ruleComm = new ParkChargeRuleComm();
        ruleComm.setParkId(parkId);
        List<ParkChargeRuleComm> parkChargeRuleCommList = chargeRuleCommService.selectParkChargeRuleCommList(ruleComm);
        AjaxResult ruleRes = chargeRuleCommandService.downChargeRuleCommList(parkId, sn, parkChargeRuleCommList);
        if (ruleRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) ruleRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downChargeRuleCommList]收费规则下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "收费规则下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发车辆管理类型通行权限
     */
    private boolean downMngTypePassAuth(Long parkId, String sn) {
        ParkCarMngtypeLane parkCarMngtypeLane = new ParkCarMngtypeLane();
        parkCarMngtypeLane.setParkId(parkId);
        List<ParkCarMngtypeLane> passAuthList = parkCarMngtypeLaneService.selectParkCarMngtypeLaneList(parkId, parkCarMngtypeLane);
        AjaxResult passAuthRes = parkCarMngTypeCommandService.downMngTypePassAuth(parkId, sn, passAuthList);
        if (!passAuthRes.get(Constants.RESULT_CODE).equals(CommandResultCode.OK)
                && !passAuthRes.get(Constants.RESULT_CODE).equals(CommandResultCode.REQ_RSP)
                && !passAuthRes.get(Constants.RESULT_CODE).equals(CommandResultCode.REQ_NORSP)) {
            String failMsg = (String) passAuthRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downMngTypePassAuth]管理类型通行权限参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "通行权限下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发车辆管理类型
     */
    private boolean downMngType(Long parkId, String sn) {
        ParkCarMngtype parkCarMngtype = new ParkCarMngtype();
        parkCarMngtype.setParkId(parkId);
        List<ParkCarMngtype> mngTypeList = parkCarMngtypeService.selectParkCarMngtypeList(parkCarMngtype);
        AjaxResult mngTypeRes = parkCarMngTypeCommandService.downMngType(parkId, sn, mngTypeList);
        if (mngTypeRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) mngTypeRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downMngType]车辆管理类型参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "管理类型下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发副摄像机
     */
    private boolean downSubIpc(Long parkId, String sn) {
        ParkLane parkLane = parkIpcBizService.getIpcLane(parkId, sn);
        parkLane.setLaneQrcode(parkGarageLaneDevBizService.laneQrCode(parkLane));

        ParkDeviceNew dev = new ParkDeviceNew();
        dev.setLaneId(parkLane.getId());
        dev.setIsMain(Constants.NO);
        dev.setStatus(Constants.NORMAL);
        dev.setRecordStatus(Constants.NORMAL);
        List<ParkDeviceNew> subIpcList = parkDeviceNewService.selectParkDeviceNewList(dev);
        AjaxResult subIpcRes = downSubIpc(parkId, sn, subIpcList, "");
        if (subIpcRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) subIpcRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downSubIpc]副摄像机参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "副摄像机下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发附加设备
     */
    private boolean downAdditiveEquipment(Long parkId, String sn) {
        // 根据 sn 获取通道
        ParkDeviceNew parkDeviceNew = parkDeviceNewService.selectParkDeviceNewByCode(sn);
        long laneId = parkDeviceNew.getLaneId();
        // 获取通道下的所有附加设备
        ParkDeviceSub search = new ParkDeviceSub();
        search.setParkId(parkId);
        search.setLaneId(laneId);
        search.setStatus(Constants.NORMAL);
        search.setRecordStatus(Constants.NORMAL);
        List<ParkDeviceSub> subDevList = parkDeviceSubService.selectParkDeviceSubList(search);
        // 下发
        AjaxResult subDevRes = downSubDevs(parkId, sn, subDevList, "");
        if (subDevRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) subDevRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downAdditiveEquipment]附加设备参数下发失败，车场ID：{}，通道ID：{}，主相机编号：{}，失败原因：{}", parkId, laneId, sn, failMsg);
            String resultMsg = "附加设备下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发LED屏参数
     */
    private boolean setLed(Long parkId, String sn) {
        ParkLane parkLane = parkIpcBizService.getIpcLane(parkId, sn);
        parkLane.setLaneQrcode(parkGarageLaneDevBizService.laneQrCode(parkLane));
        AjaxResult ledRes = setLed(parkId, parkLane, "");
        if (ledRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) ledRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#setLed]显示屏参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, ledRes.get(AjaxResult.MSG_TAG));
            String resultMsg = "LED屏显下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发通道参数
     */
    private boolean downParkLane(Long parkId, String sn) {
        ParkLane parkLane = parkIpcBizService.getIpcLane(parkId, sn);
        parkLane.setLaneQrcode(parkGarageLaneDevBizService.laneQrCode(parkLane));
        AjaxResult laneRes = parkLaneCommandService.downParkLane(parkId, sn, parkLane);
        if (laneRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) laneRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downParkLane]通道参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "通道参数下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发车库参数
     */
    private boolean downSingleDevParkGarageList(Long parkId, String sn) {
        ParkGarage garage = new ParkGarage();
        garage.setParkId(parkId);
        List<ParkGarage> garageList = parkGarageService.selectParkGarageList(parkId, garage);
        AjaxResult garageRes = parkGarageCommandService.downSingleDevParkGarageList(parkId, sn, garageList);
        if (garageRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) garageRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downSingleDevParkGarageList]车库参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "车库参数下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 下发车场参数
     */
    private boolean downSingleDevParkConfig(Long parkId, String sn) {
        ParkConfig parkConfig = new ParkConfig();
        parkConfig.setParkId(parkId);
        List<ParkConfig> configList = parkConfigService.selectParkConfigList(parkConfig);
        ParkConfig config = CollectionUtils.isNotEmpty(configList) ? configList.get(0) : null;
        AjaxResult parkRes = parkConfigCommandService.downSingleDevParkConfig(sn, config);
        if (parkRes.get(AjaxResult.CODE_TAG).equals(HttpStatus.COMMAND_ERROR)) {
            String failMsg = (String) parkRes.get(AjaxResult.MSG_TAG);
            log.warn("[IParkIpcCommandService#downSingleDevParkConfig]车场参数下发失败，车场ID：{}，设备编号：{}，失败原因：{}", parkId, sn, failMsg);
            String resultMsg = "车场参数下发失败，" + failMsg + Constants.BR;
            StringBuffer commandFailMsg = commandFailMap.get(sn);
            commandFailMsg.append(resultMsg);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public PageResult getCommandInfoList(CommandListRequest request) {
        IPage<CommandInfoVo> page = commandInfoService.findByPage(request);
        List<CommandInfoVo> commandInfoList = page.getRecords();

        return PageResult.builder()
                .pageNum(request.getPageNum())
                .pageSize(request.getPageSize())
                .totalSize(page.getTotal())
                .totalPages(page.getPages())
                .list(commandInfoList)
                .build();
    }

    @Override
    public List<ParkDeviceNew> getMainDevicesByParkId(Long parkId) {
        ParkDeviceNew parkDeviceNew = new ParkDeviceNew();
        parkDeviceNew.setParkId(parkId);
        parkDeviceNew.setStatus("normal");
        parkDeviceNew.setIsMain("Y");
        return parkDeviceNewService.selectParkDeviceNewList(parkDeviceNew);
    }

    @Override
    public AjaxResult downMngType(Long parkId, List<ParkDeviceNew> deviceList, List<ParkCarMngtype> mngtypeList, String version) {
        if (CollUtil.isNotEmpty(deviceList)) {
            StringBuilder resultMsg = new StringBuilder();
            for (ParkDeviceNew deviceNew : deviceList) {
                if (StringUtils.isNotEmpty(deviceNew.getCode())) {
                    AjaxResult result = downMngType(parkId, deviceNew.getCode(), mngtypeList, version);
                    if (null != result && null != result.get(Constants.MESSAGE)) {
                        String message = (String) result.get(Constants.MESSAGE);
                        resultMsg.append(deviceNew.getName()).append(message).append(Constants.BR);
                    }
                }
            }
            return AjaxResult.success(resultMsg.toString());
        } else {
            return AjaxResult.success("车场没有添加主相机，无需下发");
        }
    }

    @Override
    public void updateByCmd(CommandInfo commandInfo) {
        LambdaQueryWrapper<CommandInfo> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(CommandInfo::getCmd, commandInfo.getCmd());
        commandInfoMapper.update(commandInfo, queryWrapper);
    }

    /**
     * 单个设备下载车辆管理类型
     *
     * @param sn   设备编号
     * @param list 车辆管理类型列表
     */
    private AjaxResult downMngType(Long parkId, String sn, List<ParkCarMngtype> list, String version) {
        JSONObject dataObj = new JSONObject();
        dataObj.put(CommandConstant.LIST, list);
        List<Long> recIdList = list.stream().map(ParkCarMngtype::getId).collect(Collectors.toList());
        dataObj.put(CommandConstant.REC_IDS, recIdList);

        JSONObject obj = getBaseObj(parkId, sn, ParkApiName.COMMAND_MNG_TYPE_DOWNLOAD, version);
        obj.put(CommandConstant.DATA, dataObj);

        @SuppressWarnings("unchecked")
        Map<String, Object> params = JSON.toJavaObject(obj, Map.class);

        return parkCarMngTypeCommandApi.downMngType(params);
    }

}
